package com.netty.demo.udp;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;
import io.netty.util.NetUtil;

/**
 * UDP组播 Server
 * 224.0.0.0～224.0.0.255        为预留的组播地址（永久组地址），地址224.0.0.0保留不做分配，其它地址供路由协议使用；
 * 224.0.1.0～224.0.1.255        是公用组播地址，可以用于Internet；欲使用需申请。
 * 224.0.2.0～238.255.255.255    为用户可用的组播地址（临时组地址），全网范围内有效；
 * 239.0.0.0～239.255.255.255    为本地管理组播地址，仅在特定的本地范围内有效。
 *
 * @author zhangtao
 */
public class MultiCastServer {


    public static void main(String[] args) {
        System.setProperty("java.net.preferIPv4Stack", "true");
        // 组播地址
        InetSocketAddress groupAddress = new InetSocketAddress("224.2.1.1", 9000);

        EventLoopGroup group = new NioEventLoopGroup();
        try {
            NetworkInterface ni = NetUtil.LOOPBACK_IF;
            Enumeration<InetAddress> addresses = ni.getInetAddresses();
            InetAddress localAddress = null;
            while (addresses.hasMoreElements()) {
                InetAddress address = addresses.nextElement();
                if (address instanceof Inet4Address) {
                    localAddress = address;
                }
            }
            System.out.println(localAddress);
            Bootstrap bootstrap = new Bootstrap();
            //设置NioDatagramChannel
            bootstrap.group(group)
                    .channel(NioDatagramChannel.class)
                    .localAddress(localAddress, groupAddress.getPort())
                    //设置Option 组播
                    .option(ChannelOption.IP_MULTICAST_IF, ni)
                    //设置Option 地址
                    .option(ChannelOption.IP_MULTICAST_ADDR, localAddress)
                    //设置地址
                    .option(ChannelOption.SO_REUSEADDR, true)
                    .handler(new ChannelInitializer<NioDatagramChannel>() {
                        @Override
                        public void initChannel(NioDatagramChannel ch) throws Exception {
                            ChannelPipeline addLast = ch.pipeline().addLast();
                            addLast.addLast(new SimpleChannelInboundHandler<DatagramPacket>() {//接收信息处理

                                @Override
                                protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
                                    // 打印一句话
                                    System.out.println(msg.sender() + " >>> " + new String(msg.content().toString(CharsetUtil.UTF_8)));
                                }
                            });
                        }
                    });

            //获取NioDatagramChannel
            NioDatagramChannel channel = (NioDatagramChannel) bootstrap.bind(groupAddress.getPort()).sync().channel();
            //加入组
            channel.joinGroup(groupAddress, ni).sync();
            //关闭Channel
            channel.closeFuture().await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();//优雅退出
        }
    }
}